/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package compiler.parser;

import compiler.DotDump;
import compiler.grammar.Grammar;
import compiler.grammar.NonTerminal;
import compiler.grammar.Production;
import compiler.grammar.Terminal;
import compiler.scanner.NfaToDfaAdapter;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;

/**
 *
 * @author meemo
 */
public class ParserGen
{
    //~ Instance fields --------------------------------------------------------

    private final Grammar grammar;

    //~ Constructors -----------------------------------------------------------

    public ParserGen(Grammar aGrammar)
    {
        grammar = aGrammar;
    }

    //~ Methods ----------------------------------------------------------------

    public void process(String path, String testName) throws Exception
    {
        NfaToDfaAdapter dfaGen = new NfaToDfaAdapter(grammar.getTerminals());
        dfaGen.getDfaTable();
        computeNullability(grammar.getNonTerminals());
        computeFirstSets(grammar.getNonTerminals());

        LALRParsingTable parsingTable = getParsingTable();

        PrintWriter      writer = new PrintWriter(new FileOutputStream(path + testName + "_a_nfa.dot"));
        writer.println(DotDump.dumpDot(dfaGen.getNfaMachine()));
        writer.close();

        writer = new PrintWriter(new FileOutputStream(path + testName + "_b_dfa.dot"));
        writer.println(DotDump.dumpDot(dfaGen.getDfaNormal()));
        writer.close();

        writer = new PrintWriter(new FileOutputStream(path + testName + "_c_dfamini.dot"));
        writer.println(DotDump.dumpDot(dfaGen.getDfaMini()));
        writer.close();

        if (!testName.contains("5")) // test 5 is very big for drawing
        {
            writer = new PrintWriter(new FileOutputStream(path + testName + "_d_lalr.dot"));
            writer.println(DotDump.dumpDot(parsingTable));
            writer.close();
        }

        //        System.out.println(parsingTable);
    }

    private void computeFirstSets(ArrayList<NonTerminal> nonTerminals)
    {
        boolean changed = true;

        while (changed)
        {
            changed = false;

            for (NonTerminal nonTerminal : nonTerminals)
            {
                for (Production production : nonTerminal.getProductions())
                {
                    if (production.computeFirstSet())
                    {
                        changed = true;
                        nonTerminal.getFirstSet().addAll(production.getFirstSet());
                    }
                }
            }
        }
    }

    private void computeNullability(ArrayList<NonTerminal> nonTerminals)
    {
        boolean changed = false;

        do
        {
            changed = false;

            for (NonTerminal nonTerminal : nonTerminals)
            {
                changed = nonTerminal.updateNullability();
            }
        }
        while (changed);
    }

    private LALRParsingTable getParsingTable() throws Exception
    {
        Production augmented = new Production(null);
        augmented.add(grammar.getStartNonTerminal().getName());
        // resolve it so it'll be able to add the EOF terminal
        augmented.resolveBody( /*unused,dummy*/
        grammar.getMap());
        augmented.add(Terminal.EOF);

        LALREngine engine = new LALREngine(grammar.getMap(), augmented);

        return new LALRParsingTableGen(engine).getParsingTable();
    }
}
